
package uk.addie.mp3fixer;

import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Level;

import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.CannotWriteException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.tag.FieldDataInvalidException;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.KeyNotFoundException;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.TagException;
import org.jaudiotagger.tag.id3.AbstractID3Tag;
import org.jaudiotagger.tag.id3.ID3v1Tag;
import org.jaudiotagger.tag.id3.ID3v23Tag;

final public class MP3Handle {
    private static int stripString(final String value) {
        if (value.length() == 0) {
            return 0;
        }
        final int slash = value.indexOf('/');
        if (slash == -1) {
            return Integer.valueOf(value);
        }
        return Integer.valueOf(value.substring(0, slash));
    }

    public static MP3Handle withPath(final Path path) {
        try {
            return new MP3Handle(path);
        } catch (IOException e) {
            MP3Fixer.LOG.log(Level.SEVERE, "Can't open MP3", e);
            return null;
        }
    }

    private AudioFile  mAudiofile;
    private boolean    mChanged;
    private Tag        tag;
    private final Path path;

    private MP3Handle(final Path path) throws IOException {
        this.path = path;
        try {
            mAudiofile = AudioFileIO.read(path.toFile());
            tag = mAudiofile.getTag();
            if (tag instanceof AbstractID3Tag && !(tag instanceof ID3v23Tag)) {
                MP3Fixer.LOG.warning("Oldschool tagging");
                tag = new ID3v23Tag((AbstractID3Tag) tag);
                mAudiofile.setTag(tag);
                mAudiofile.commit();
            }
        } catch (CannotReadException | TagException | ReadOnlyFileException | InvalidAudioFrameException
                | CannotWriteException e) {
            MP3Fixer.LOG.log(Level.SEVERE, "Can't read MP3", e);
        }
    }

    public StringBuilder canonicalName() {
        final StringBuilder newName = new StringBuilder();
        if (discInt() != 0) {
            newName.append(String.format("%02d-", discInt()));
        }
        if (trackInt() != 0) {
            newName.append(String.format("%02d ", trackInt()));
        }
        newName.append(Util.clean(getSongTitle()));
        newName.append('.');
        newName.append(Util.extension(path.getFileName().toString()));
        return newName;
    }

    private int discInt() {
        return stripString(getTag(FieldKey.DISC_NO));
    }

    public String getAlbum() {
        return getTag(FieldKey.ALBUM);
    }

    public String getArtist() {
        return getTag(FieldKey.ARTIST);
    }

    public String getSongTitle() {
        return getTag(FieldKey.TITLE);
    }

    public String getTag(final FieldKey fk) {
        if (tag == null) {
            return "";
        }
        final String rval = tag.getFirst(fk);
        if (rval == null) {
            return "";
        }
        return rval;
    }

    public String getTrack() {
        return getTag(FieldKey.TRACK);
    }

    public boolean isCanonical() {
        return path.getFileName().equals(canonicalName());
    }

    public boolean isChanged() {
        return mChanged;
    }

    public boolean isCompilation() {
        return "1".equals(getTag(FieldKey.IS_COMPILATION));
    }

    public boolean isMP3() {
        return tag != null;
    }

    public MP3Handle save() {
        if (mChanged && (tag != null)) {
            try {
                mAudiofile.commit();
            } catch (CannotWriteException e) {
                MP3Fixer.LOG.log(Level.SEVERE, "Can't save", e);
            }
        }
        mChanged = false;
        return this;
    }

    public MP3Handle setAlbum(final String album) {
        setTag(FieldKey.ALBUM, album);
        return this;
    }

    public MP3Handle setArtist(final String artist) {
        setTag(FieldKey.ARTIST, artist);
        return this;
    }

    public MP3Handle setSongTitle(final String title) {
        setTag(FieldKey.TITLE, title);
        return this;
    }

    public MP3Handle setTag(final FieldKey key, final String value) {
        if (value.equals(tag.getFirst(key))) {
            return this;
        }
        try {
            if ("".equals(value)) {
                tag.deleteField(key);
            } else {
                tag.setField(key, value);
            }
            mChanged = true;
        } catch (KeyNotFoundException | FieldDataInvalidException e) {
            if (tag instanceof ID3v1Tag) {
                tag = new ID3v23Tag((ID3v1Tag) tag);
                mAudiofile.setTag(tag);
                setTag(key, value);
            } else {
                MP3Fixer.LOG.log(Level.SEVERE, "Can't set tag", e);
            }
        }
        return this;
    }

    public MP3Handle setTrack(final String track) {
        setTag(FieldKey.TRACK, track);
        return this;
    }

    @Override public String toString() {
        return tag.toString();
    }

    private int trackInt() {
        return stripString(getTag(FieldKey.TRACK));
    }
}
